package com.wuyan.web.form.api;

import com.alibaba.excel.EasyExcel;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wuyan.web.base.aop.ApiLogAnnotation;
import com.wuyan.web.base.helper.BaseApi;
import com.wuyan.web.base.helper.rep.RepBody;
import com.wuyan.web.base.helper.rep.RepCodeEnum;
import com.wuyan.web.base.helper.rep.RepHelper;
import com.wuyan.web.base.helper.rep.RepPageData;
import com.wuyan.web.form.entity.PubForm;
import com.wuyan.web.form.helper.easyexcel.ContentStyleWriteHandler;
import com.wuyan.web.form.helper.easyexcel.HeadStyleWriteHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 导出接口
 */

@Slf4j
@RestController
@RequestMapping("/api/export")
public class ExportApi extends BaseApi implements RepHelper {

    @Autowired
    private FormApi formApi;

    @Autowired
    private ObjectMapper mapper;

    /**
     * 根本表单导出指定数据
     */
    @GetMapping(value = "/{table}")
    @ApiLogAnnotation(name = "FormData:export")
    public void exportTable(HttpServletRequest request, HttpServletResponse response,
                            @PathVariable(value = "table") String table,
                            @RequestParam(value = "params", required = false, defaultValue = "[]") String params,
                            @RequestParam(value = "orders", required = false, defaultValue = "[]") String ordersParams)
            throws IOException, ClassNotFoundException, NoSuchMethodException,
            InstantiationException, IllegalAccessException, InvocationTargetException {
        ServletOutputStream ops = response.getOutputStream();

        RepBody<PubForm> pubFormRepBody = formApi.exists(table);
        if (null == pubFormRepBody.getData()) {
            String msg = mapper.writeValueAsString(error(RepCodeEnum.ERR_NO_FORM));
            ops.write(msg.getBytes());
            return;
        }

        RepBody<RepPageData<Map>> res = formApi.page(table, false, 1, 0, params, ordersParams);
        if (res.getCode() != RepCodeEnum.OK.getCode()) {
            String msg = mapper.writeValueAsString(res);
            ops.write(msg.getBytes());
            return;
        }

        // 基础数据
        PubForm pubForm = pubFormRepBody.getData();
        List<Map> data = res.getData().getList();
        List<Map> fields = mapper.readValue(pubForm.getFields(), new TypeReference<List<Map>>() {
        });

        // 组装成easy excel 需要的
        List<String[]> fieldNames = fieldNames(fields);

        // 数据区
        List<List<String>> records = dataList(fieldNames, data);
        // 将头部和数据结合在一起输出
        records.add(0, headData(fieldNames));

        // 文件名称
        String sheetName = pubForm.getName() + "-" + LocalDateTime.now();

        response.setContentType("application/ms-excel;charset=UTF-8");
        response.setCharacterEncoding("utf-8");
        // 防止中文乱码
        String fileName = URLEncoder.encode(sheetName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");

        EasyExcel.write(response.getOutputStream())
                .registerWriteHandler(new HeadStyleWriteHandler(headData(fieldNames)))
                .registerWriteHandler(new ContentStyleWriteHandler(headData(fieldNames), records))
                .sheet(pubForm.getName())
                .doWrite(records);
    }

    /**
     * 组装easy excel需要的表头
     *
     * @param fieldNames 字段名称与别名关系,0: 字段名，1：别名
     * @return List<List < String>>
     */
    private List<List<String>> head(List<String[]> fieldNames) {
        List<List<String>> list = new ArrayList<>();
        List<String> head0 = new ArrayList<>();
        head0.add("编号");
        list.add(head0);

        fieldNames.forEach(item -> {
            List<String> head = new ArrayList<>();
            head.add(item[1]);
            list.add(head);
        });

        return list;
    }

    /**
     * 封装头部数据
     *
     * @param fieldNames 头部信息
     * @return List<String>
     */
    private List<String> headData(List<String[]> fieldNames) {
        List<String> head = new ArrayList<>();

        head.add("编号");
        fieldNames.forEach(item -> {
            head.add(item[1]);
        });

        return head;
    }

    /**
     * 组装easy excel需要的数据
     *
     * @param fieldNames 字段名称与别名关系,0: 字段名，1：别名
     * @param data       数据
     * @return List<List < Object>>
     */
    private List<List<String>> dataList(List<String[]> fieldNames, List<Map> data) {
        List<List<String>> list = new ArrayList<>();

        data.forEach(item -> {
            List<String> record = new ArrayList<>();
            record.add(item.get("id").toString());

            fieldNames.forEach(head -> {
                String modelName = head[0];

                // 存在小数点，则代表子项
                if (modelName.contains(".")) {
                    String[] modelNames = modelName.split("\\.");
                    Object value = null;
                    for (String name : modelNames) {
                        // 不存在并且首次遍历
                        if (!item.containsKey(name) && null == value) {
                            break;
                        }
                        // 存在且首次遍历
                        else if (item.containsKey(name) && null == value) {
                            value = item.get(name);
                        } else {
                            // 如果是mao对象，则继续
                            if (value instanceof Map) {
                                Map d = (Map) value;
                                value = d.get(name);
                            }
                        }
                    }

                    if (value instanceof Collection) {
                        value = StringUtils.join((Collection<?>) value, ",");
                    } else {
                        value = null == value ? " " : value.toString();
                    }

                    record.add(value.toString());
                } else {
                    Object value = item.get(head[0]);

                    if (value instanceof Collection) {
                        value = StringUtils.join((Collection<?>) value, ",");
                    } else {
                        value = null == value ? " " : value.toString();
                    }

                    record.add(value.toString());
                }
            });

            list.add(record);
        });


        return list;
    }

    /**
     * 获取所有字段的名称
     *
     * @param fields 字段列表
     * @return String
     */
    private List<String[]> fieldNames(List<Map> fields) {
        List<String[]> list = new ArrayList<>();

        for (Map field : fields) {
            Map config = (Map) field.get("__config__");
            String label = (String) config.get("label");
            String fieldName = field.get("__vModel__").toString();
            list.add(new String[]{fieldName, label});
        }

        list.add(new String[]{"creator_name", "创建人"});
        list.add(new String[]{"create_time", "创建时间"});

        return list;
    }

}
